package xyz.app.logic;

import lombok.*;
import lombok.extern.slf4j.Slf4j;
import xyz.app.accessors.DbReader;
import xyz.app.AbstractDomain;
import xyz.app.MutableDomain;

import java.util.Objects;

@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class PersistenceLogic<T extends AbstractDomain> {

    @NonNull
    private final DbReader<T> reader;

    @Getter
    private T input, output;

    @NonNull
    private PersistenceAction action = PersistenceAction.UNDEFINED;

    public static <T extends AbstractDomain> PersistenceLogic<T> of(@NonNull DbReader<T> reader) {
        return new PersistenceLogic<>(reader);
    }

    public @NonNull PersistenceLogic<T> process(T t) {

        input = t;

        if (null == input || input.isEmpty()) {
            action = PersistenceAction.NULLIFY;
            log.info("Processing null or empty entity before persistence");
        }

        else {
            log.info("Processing entity before persistence: [{}]", input.string());

            reader.findEqualsCustom(input)
                    .filter(found -> input.isNew() || Objects.equals(input, found))
                    .ifPresentOrElse(
                            this::processEqualsCustomFound,
                            this::processEqualCustomLacks);
        }

        action.log(this);

        return this;
    }

    private void processEqualsCustomFound(T found) {
        log.info("Found equals custom sibling [{}] for entity [{}]", found.string(), input.string());
        action = input.isNew() ? PersistenceAction.REPLACE : PersistenceAction.KEEP;
        output = found;
    }

    private void processEqualCustomLacks() {
        log.info("Entity does not have an equals custom sibling in database: [{}]", input.string());

        action =
                  input.isNew() ? PersistenceAction.PERSIST
                : reader instanceof MutableDomain ? PersistenceAction.MERGE
                : PersistenceAction.SAVE_COPY;

        output = Objects.equals(action, PersistenceAction.SAVE_COPY) ?
                reader.domainClass().cast(input.copy()) : input;
    }
}